package com.yuyou.fn.educational.service.impl;

import com.github.wens.mybatisplus.examples.Example;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.entity.BaseGrade;
import com.yuyou.fn.educational.entity.CourseClass;
import com.yuyou.fn.educational.entity.Period;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.educational.vo.ContinueClassSettingVo;
import com.yuyou.fn.educational.vo.ContinueClassSuggestResultVo;
import com.yuyou.fn.educational.vo.StudentHistoryRegInfoVo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutionException;

@Service
public class ContinueClassSuggestionServiceImpl implements IContinueClassSuggestionService {

    @Resource
    private IPeriodService periodService ;

    @Resource
    private IContinueClassSettingService continueClassSettingService;

    @Resource
    private IStudentRegService studentRegService ;

    @Resource
    private ICourseClassService courseClassService;

    @Resource
    private IBaseGradeService baseGradeService ;

    @Override
    public List<ContinueClassSuggestResultVo> suggest(Long studentId) {

        List<Period> allCurrentPeriodList  = periodService.findAllCurrentPeriods();
        List<Period> allCurrentPrePeriodList= prePeriods(allCurrentPeriodList) ;

        List<Long> allCurrentPeriodIdList = BeanUtils.toList(allCurrentPeriodList,"periodId");
        List<Long> allCurrentPrePeriodIdList = BeanUtils.toList(allCurrentPrePeriodList,"periodId");
        List<Long> fromPeriodIdList = allCurrentPeriodIdList ;
        List<Long> toPeriodIdList = new ArrayList<>(allCurrentPeriodIdList.size());
        fromPeriodIdList.addAll(allCurrentPrePeriodIdList);
        List<ContinueClassSettingVo> continueClassSettingVoList = continueClassSettingService.findBaseContinueClassSettingByPeriodIds(fromPeriodIdList);

        for(ContinueClassSettingVo continueClassSettingVo : continueClassSettingVoList){
            for(ContinueClassSettingVo.Item item : continueClassSettingVo.getItemList() ){
                toPeriodIdList.add(item.regPeriodId);
            }
        }

        List<Long> relPeriodIdList  = fromPeriodIdList ;
        relPeriodIdList.addAll(toPeriodIdList);
        Map<Long,ContinueClassSettingVo> periodId2ContinueClassSettingVoMap =  BeanUtils.toMap(continueClassSettingVoList,"periodId");
        List<StudentHistoryRegInfoVo> historyRegInfoVoList = studentRegService.findStudentHistoryRegInfo(studentId, relPeriodIdList);
        if(historyRegInfoVoList.isEmpty() ){
            return Collections.EMPTY_LIST ;
        }
        List<ContinueClassSuggestResultVo> continueClassSuggestResultVos = suggestBase(fromPeriodIdList, periodId2ContinueClassSettingVoMap, historyRegInfoVoList);
        if(!continueClassSuggestResultVos.isEmpty()){
            //推荐更高层次课程
            List<ContinueClassSuggestResultVo> continueClassSuggestResultVos1 = suggestMoreLevel(continueClassSuggestResultVos);
            continueClassSuggestResultVos.addAll(continueClassSuggestResultVos1);

            //推荐其他科目
            List<ContinueClassSuggestResultVo> continueClassSuggestResultVos2 = suggestMoreSubject(continueClassSuggestResultVos);

            continueClassSuggestResultVos.addAll(continueClassSuggestResultVos2);
        }

        return continueClassSuggestResultVos;
    }

    private List<ContinueClassSuggestResultVo> suggestBase(List<Long> fromPeriodIdList, Map<Long, ContinueClassSettingVo> periodId2ContinueClassSettingVoMap, List<StudentHistoryRegInfoVo> historyRegInfoVoList) {
        Map<Long,List<StudentHistoryRegInfoVo>> periodId2StudentHistoryRegInfoVoGroup =  BeanUtils.toGroup(historyRegInfoVoList,"periodId");
        List<ContinueClassSuggestResultVo> continueClassSuggestResultVos  = new ArrayList<>(10);
        for(Long periodId : Utils.unique(fromPeriodIdList) ){
            List<StudentHistoryRegInfoVo> studentHistoryRegInfoVos = periodId2StudentHistoryRegInfoVoGroup.get(periodId);
            if(studentHistoryRegInfoVos == null ){
                continue ;
            }

            ContinueClassSettingVo continueClassSettingVo = periodId2ContinueClassSettingVoMap.get(periodId);
            if(continueClassSettingVo == null ){
                continue;
            }

            for(ContinueClassSettingVo.Item item : continueClassSettingVo.getItemList() ){

                for( StudentHistoryRegInfoVo fromStudentHistoryRegInfoVo : studentHistoryRegInfoVos ){
                    boolean hasReg  = false ;
                    List<StudentHistoryRegInfoVo> toStudentHistoryRegInfoVos = periodId2StudentHistoryRegInfoVoGroup.get(item.regPeriodId);
                    if(toStudentHistoryRegInfoVos != null){
                        for( StudentHistoryRegInfoVo toStudentHistoryRegInfoVo : toStudentHistoryRegInfoVos ){
                            if(item.isSameCampus && !fromStudentHistoryRegInfoVo.getCampusId().equals(toStudentHistoryRegInfoVo.getCampusId())){
                                continue;
                            }

                            if(item.isSameSubject && !fromStudentHistoryRegInfoVo.getSubjectId().equals(toStudentHistoryRegInfoVo.getSubjectId())){
                                continue;
                            }

                            if(item.isSameLevel && !fromStudentHistoryRegInfoVo.getLevel().equals(toStudentHistoryRegInfoVo.getLevel())){
                                continue;
                            }

                            if(item.isSameGrade && !fromStudentHistoryRegInfoVo.getGradeId().equals(toStudentHistoryRegInfoVo.getGradeId())){
                                continue;
                            }

                            if(!item.isSameGrade && !fromStudentHistoryRegInfoVo.getGradeId().equals(nextGradeId(toStudentHistoryRegInfoVo.getGradeId() ))){
                                continue;
                            }

                            hasReg = true;
                            break;
                        }
                    }

                    if(!hasReg){//没有报读过，系统推荐

                        Example<CourseClass> courseClassExample = CourseClass.createExample();
                        Example.Criteria criteria = courseClassExample.createCriteria();

                        criteria.andEqualTo("periodId" , item.regPeriodId ).andEqualTo("status",1 );


                        if(item.isSameCampus){
                            criteria.andEqualTo("campusId" , fromStudentHistoryRegInfoVo.getCampusId() );
                        }

                        if(item.isSameSubject){
                            criteria.andEqualTo("subjectId" , fromStudentHistoryRegInfoVo.getSubjectId());
                        }

                        if(item.isSameGrade ){
                            criteria.andEqualTo("gradeId" , fromStudentHistoryRegInfoVo.getGradeId() );
                        }else{
                            criteria.andEqualTo("gradeId" , nextGradeId( fromStudentHistoryRegInfoVo.getGradeId() ) );
                        }

                        criteria.andLessThanOrEqualTo("level" , fromStudentHistoryRegInfoVo.getLevel() );
                        criteria.andIn("level" , Arrays.asList(2,3,4,5)) ;
                        courseClassExample.orderBy("level").desc().orderBy("studyingTime");
                        courseClassExample.selectProperties("classId" ,"subjectId" ,"gradeId" ,"periodId" ,"level" ,"campusId");
                        CourseClass suggestCourseClass = courseClassService.findOneByExample(courseClassExample);
                        if(suggestCourseClass != null ){
                            continueClassSuggestResultVos.add(new ContinueClassSuggestResultVo(suggestCourseClass.getClassId(),suggestCourseClass.getPeriodId(),suggestCourseClass.getCampusId(),suggestCourseClass.getLevel(),suggestCourseClass.getSubjectId(),suggestCourseClass.getGradeId(),1));
                        }
                    }

                }
            }
        }
        return continueClassSuggestResultVos;
    }

    private List<ContinueClassSuggestResultVo> suggestMoreSubject(List<ContinueClassSuggestResultVo> continueClassSuggestResultVos) {
        List<Long> campusIds  = BeanUtils.toList(continueClassSuggestResultVos , "campusId");
        List<Long> periodIds  = BeanUtils.toList(continueClassSuggestResultVos , "periodId");
        Map<String,List<ContinueClassSuggestResultVo>> campusIdPeriodId2ContinueClassSuggestResultVo =  BeanUtils.toGroup(continueClassSuggestResultVos, new Function<ContinueClassSuggestResultVo, String>() {
            @Override
            public String apply(ContinueClassSuggestResultVo input) {
                return String.valueOf(input.getCampusId()) + String.valueOf(input.getPeriodId());
            }
        });
        List<ContinueClassSuggestResultVo> continueClassSuggestResultVos2  = new ArrayList<>(10);

        for(Long campusId : Utils.unique(campusIds) ){
            for(Long periodId : Utils.unique( periodIds) ){
                List<Long> subjectIds  = courseClassService.howManySubjectForCampusAndPeriod(campusId,periodId);

                List<ContinueClassSuggestResultVo> hadSuggested  = campusIdPeriodId2ContinueClassSuggestResultVo.get(String.valueOf(campusId) + String.valueOf(periodId));
                Map<Long,List<ContinueClassSuggestResultVo>> gradeId2ContinueClassSuggestResultVo = BeanUtils.toGroup(hadSuggested, "gradeId" );
                for(Map.Entry<Long,List<ContinueClassSuggestResultVo>> entry : gradeId2ContinueClassSuggestResultVo.entrySet() ){
                    Map<Long,ContinueClassSuggestResultVo> subjectId2ContinueClassSuggestResultVo = BeanUtils.toMap(entry.getValue(),"subjectId");
                    for(Long subjectId : subjectIds){
                        if(subjectId2ContinueClassSuggestResultVo.containsKey(subjectId)){
                            continue;
                        }

                        Example<CourseClass> courseClassExample = CourseClass.createExample();
                        Example.Criteria criteria = courseClassExample.createCriteria();

                        criteria.andEqualTo("periodId" , periodId ).andEqualTo("status",1 );

                        criteria.andEqualTo("campusId" , campusId );
                        criteria.andEqualTo("subjectId" , subjectId);
                        criteria.andEqualTo("gradeId" ,  entry.getKey() );
                        criteria.andIn("level" , Arrays.asList(2,3,4,5)) ;
                        courseClassExample.selectProperties("classId" ,"subjectId" ,"gradeId" ,"periodId" ,"level" ,"campusId");
                        courseClassExample.orderBy("level").asc().orderBy("studyingTime");
                        CourseClass suggestCourseClass = courseClassService.findOneByExample(courseClassExample);

                        if(suggestCourseClass != null ){
                            continueClassSuggestResultVos2.add(new ContinueClassSuggestResultVo(suggestCourseClass.getClassId(),suggestCourseClass.getPeriodId(),suggestCourseClass.getCampusId(),suggestCourseClass.getLevel(),suggestCourseClass.getSubjectId(),suggestCourseClass.getGradeId(),2 ));
                        }

                    }
                }

            }
        }
        return continueClassSuggestResultVos2;
    }

    private List<ContinueClassSuggestResultVo> suggestMoreLevel(List<ContinueClassSuggestResultVo> continueClassSuggestResultVos) {
        List<ContinueClassSuggestResultVo> continueClassSuggestResultVos1  = new ArrayList<>(10);
        for(ContinueClassSuggestResultVo continueClassSuggestResultVo : continueClassSuggestResultVos ){
            Example<CourseClass> courseClassExample = CourseClass.createExample();
            Example.Criteria criteria = courseClassExample.createCriteria();

            criteria.andEqualTo("periodId" , continueClassSuggestResultVo.getCampusId() ).andEqualTo("status",1 );

            criteria.andEqualTo("campusId" , continueClassSuggestResultVo.getCampusId() );
            criteria.andEqualTo("subjectId" , continueClassSuggestResultVo.getSubjectId());
            criteria.andEqualTo("gradeId" , continueClassSuggestResultVo.getGradeId() );
            criteria.andIn("level" , Arrays.asList(2,3,4,5)) ;
            criteria.andLessThanOrEqualTo("level" , continueClassSuggestResultVo.getLevel() - 1 );
            courseClassExample.orderBy("studyingTime");
            courseClassExample.selectProperties("classId" ,"subjectId" ,"gradeId" ,"periodId" ,"level" ,"campusId");
            courseClassExample.orderBy("level").desc().orderBy("studyingTime");
            CourseClass suggestCourseClass = courseClassService.findOneByExample(courseClassExample);

            if(suggestCourseClass != null ){
                continueClassSuggestResultVos1.add(new ContinueClassSuggestResultVo(suggestCourseClass.getClassId(),suggestCourseClass.getPeriodId(),suggestCourseClass.getCampusId(),suggestCourseClass.getLevel(),suggestCourseClass.getSubjectId(),suggestCourseClass.getGradeId(),2));
            }
        }
        return continueClassSuggestResultVos1;
    }

    private LoadingCache<String ,Optional<Period>> prePeriodCache = CacheBuilder.newBuilder().build(new CacheLoader<String, Optional<Period>>() {
        @Override
        public Optional<Period> load(String key) throws Exception {
            String[] parts = key.split("#");
            return Optional.fromNullable(periodService.findPrePeriod(Long.valueOf(parts[0]),Integer.valueOf(parts[1])));
        }
    });

    private List<Period> prePeriods(List<Period> list) {

        prePeriodCache.invalidateAll();
        List<Period> ret = new ArrayList<>(list.size());
        for(Period period : list ){
            try {
                Optional<Period> periodOptional = prePeriodCache.get(String.valueOf(period.getAreaTeamId()) + "#" + String.valueOf(period.getPeriodNo()));
                if(periodOptional.isPresent()){
                    ret.add(periodOptional.get());
                }
            } catch (ExecutionException e) {
                //
            }

        }
        return ret ;
    }

    private LoadingCache<Long,Long> nextGradeCache = CacheBuilder.newBuilder().build(new CacheLoader<Long, Long>() {
        @Override
        public Long load(Long key) throws Exception {
            List<BaseGrade> baseGrades = baseGradeService.findAllFromCache();
            for(int i = 0 ;i < baseGrades.size() - 1 ; i++ ){
                if(baseGrades.get(i).equals(key)){
                    return baseGrades.get(i + 1 ).getGradeId() ;
                }
            }
            return key + 1 ;
        }
    });

    private Long nextGradeId(Long gradeId) {
        try{
            return nextGradeCache.get(gradeId);
        }catch (Exception e){
            return gradeId + 1 ;//可能不存在的年级
        }

    }
}
